package com.lagou.filter;

import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @author June
 * @since 2021年04月
 */
@Activate(group = {CommonConstants.CONSUMER})
public class TPMonitorFilter implements Filter, Runnable {

    final Map<String, List<MethodInfo>> map = new ConcurrentHashMap<>();

    public TPMonitorFilter() {
        Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(this, 5, 5, TimeUnit.SECONDS);
    }

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        // 开始时间
        long startTime = System.currentTimeMillis();
        Result result = null;
        long costTimes = 0L;
        try {
            // 执行方法
            result = invoker.invoke(invocation);
        } finally {
            long endTime = System.currentTimeMillis();
            costTimes = endTime - startTime;
            System.out.println("invoke times : " + costTimes + "`ms");
        }
        String methodName = invocation.getMethodName();
        List<MethodInfo> methodInfos = map.computeIfAbsent(methodName, k -> new ArrayList<>());
        methodInfos.add(new MethodInfo(invocation.getMethodName(), costTimes, System.currentTimeMillis()));
        return result;
    }

    @Override
    public void run() {
        Set<String> keySet = map.keySet();
        for (String key : keySet) {
            List<MethodInfo> methodInfos = map.get(key);
            System.out.println("method : " + key + "`TP90 : " + getTP(methodInfos, 0.9));
            System.out.println("method : " + key + "`TP99 : " + getTP(methodInfos, 0.99));
        }
    }

    private long getTP(List<MethodInfo> methodInfos, double rate) {
        List<MethodInfo> sortInfo = new ArrayList<>();
        long endTime = System.currentTimeMillis();
        long startTime = System.currentTimeMillis() - 60 * 1000;
        for (MethodInfo methodInfo : methodInfos) {
            if (methodInfo.getEndTimes() >= startTime && methodInfo.getEndTimes() <= endTime) {
                sortInfo.add(methodInfo);
            }
        }
        sortInfo.sort(Comparator.comparingLong(MethodInfo::getCostTimes));
        int index = (int) (sortInfo.size() * rate);
        return sortInfo.get(index).getCostTimes();
    }
}
